package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"miao-server/global"
	"miao-server/lib/utils"
	"miao-server/listener"
	"miao-server/structs/base"
	"miao-server/structs/dto"
	"miao-server/structs/enum"
	"os"
	"path"
	"time"
)

var InstructServer = new(instructServer)

type instructServer struct{}

func (s instructServer) ClearDB(uniqueCode string, in dto.ClearDb) (err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	bs, _ := json.Marshal(in)
	err = listener.TcpSer.SendInstruct(uniqueCode, base.ClientInstruct{
		Code: enum.ClearDB,
		Body: string(bs),
	})
	return
}

func (s instructServer) GetPcLoad(uniqueCode string, in dto.LoadInstDto) (msg dto.InstructResponseDto, err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	bs, _ := json.Marshal(in)
	t := time.Now().Unix()
	err = listener.TcpSer.SendInstruct(uniqueCode, base.ClientInstruct{
		Code: enum.LoadCount,
		Body: string(bs),
	})
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	for range ticker.C {
		var b bool
		msg, b = global.InstructResponseCache[fmt.Sprintf("%s_%s_%s", uniqueCode, fmt.Sprintf("%d", enum.LoadCount), "nil")]
		if b && msg.ServiceTime >= t {
			break
		}
		if time.Now().Unix()-t > 10 {
			err = errors.New("客户端未在指定时间内返回数据，请联系开发人员排查客户端问题！")
			logrus.Errorf("客户端未在指定时间内返回数据，指令信息：%+v,客户端举手信息：%+v", in, global.HandUpCache[uniqueCode])
			break
		}
	}
	return
}

// SendInstruct 发送指令，不需要等待结果
func (s instructServer) SendInstruct(uniqueCode string, in base.PluginInstruct) (err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	var bs []byte
	bs, err = json.Marshal(in)
	if err != nil {
		return
	}
	err = listener.TcpSer.SendInstruct(uniqueCode, base.ClientInstruct{
		Code: enum.ExecutePlugin,
		Body: string(bs),
	})
	return err
}

// SendInstructR 发送指令，等待结果，最多等待10秒
func (s instructServer) SendInstructR(uniqueCode string, in base.PluginInstruct) (msg dto.InstructResponseDto, err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	t := time.Now().Unix()
	var bs []byte
	bs, err = json.Marshal(in)
	if err != nil {
		return
	}
	err = listener.TcpSer.SendInstruct(uniqueCode, base.ClientInstruct{
		Code: enum.ExecutePlugin,
		Body: string(bs),
	})

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	for range ticker.C {
		var b bool
		msg, b = global.InstructResponseCache[fmt.Sprintf("%s_%s_%s", uniqueCode, in.Cmd, in.Args)]
		if b && msg.ServiceTime >= t {
			break
		}
		if time.Now().Unix()-t > 10 {
			err = errors.New("客户端未在指定时间内返回数据，请联系开发人员排查客户端问题！")
			logrus.Errorf("客户端未在指定时间内返回数据，指令信息：%+v,客户端举手信息：%+v", in, global.HandUpCache[uniqueCode])
			break
		}
	}
	return
}

// SendNobodyInstruct 发送不需要参数的指令，包括：HandsUp、StopGuarder、UpdateAllPolicy
func (s instructServer) SendNobodyInstruct(uniqueCode string, instType string) (err error) {
	inst := base.ClientInstruct{}
	switch instType {
	case "HandsUp":
		inst.Code = enum.HandsUp
	case "StopGuarder":
		inst.Code = enum.StopGuarder
	case "UpdateAllPolicy":
		inst.Code = enum.UpdateAllPolicy
	default:
		return
	}
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	err = listener.TcpSer.SendInstruct(uniqueCode, inst)
	return err
}

// StartGuarder 启动守卫
func (s instructServer) StartGuarder(uniqueCode string, in base.PluginInstruct) (err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	if in.Cmd == "" {
		in.Cmd = enum.GuarderName
		in.Args = []string{"-m=0.8", "-t=60"}
	}
	inst := base.ClientInstruct{}
	inst.Code = enum.StartGuarder
	var bs []byte
	bs, err = json.Marshal(in)
	if err != nil {
		return
	}
	inst.Body = string(bs)
	err = listener.TcpSer.SendInstruct(uniqueCode, inst)
	return
}

// GetPolicy 获取客户端策略
func (s instructServer) GetPolicy(uniqueCode string) (msg dto.InstructResponseDto, err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	inst := base.ClientInstruct{}
	inst.Code = enum.UploadPolicy
	t := time.Now().Unix()
	err = listener.TcpSer.SendInstruct(uniqueCode, inst)
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	for range ticker.C {
		var b bool
		msg, b = global.InstructResponseCache[fmt.Sprintf("%s_%s_%s", uniqueCode, fmt.Sprintf("%d", enum.UploadPolicy), "nil")]
		if b && msg.ServiceTime >= t {
			break
		}
		if time.Now().Unix()-t > 10 {
			err = errors.New("客户端未在指定时间内返回数据，请联系开发人员排查客户端问题！")
			logrus.Errorf("获取客户端策略失败，uniqueCode:%s", uniqueCode)
			break
		}
	}
	return
}

// SetLogLv 设置日志级别
func (s instructServer) SetLogLv(uniqueCode string, lv string) (err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	inst := base.ClientInstruct{}
	inst.Code = enum.SetLogLv
	inst.Body = lv
	err = listener.TcpSer.SendInstruct(uniqueCode, inst)
	return
}

// UpdatePlugin 通知客户端下载更新插件
func (s instructServer) UpdatePlugin(uniqueCode string, filename, uri string) (err error) {
	if !listener.TcpSer.IsOnline(uniqueCode) {
		err = errors.New("设备不在线")
	}
	fp := path.Join(global.Conf.Http.PluginFolder, filename)
	_, err = os.Lstat(fp)
	if errors.Is(err, os.ErrNotExist) {
		err = errors.New("插件不存在")
		return
	}
	md5Str := utils.GetFileMD5(path.Join(global.Conf.Http.PluginFolder, filename))
	plug := base.PluginInstruct{
		Cmd:             "update.exe",
		Args:            []string{fmt.Sprintf("-u=%s", uri), fmt.Sprintf("-m=%s", md5Str), fmt.Sprintf("-t=%s", "./plugin/")},
		TimeoutDuration: 10,
	}
	err = s.SendInstruct(uniqueCode, plug)
	return err
}

// AbnormalOlineDeviceList 获取不正常的在线设备列表
func (s instructServer) AbnormalOlineDeviceList() (li []dto.TcpClientDto) {
	all := listener.TcpSer.GetClientInfo()
	for _, item := range all {
		if item.ClientId == 0 {
			li = append(li, item)
		}
	}
	return
}

// OlineDevice 获取在线设备列表，不区分是否正常
func (s instructServer) OlineDevice() (li []dto.TcpClientDto) {
	li = listener.TcpSer.GetClientInfo()
	return li
}
